using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

namespace HadronData
{
    public class Circle
        : Geometry
    {
        private Vector2 m_vecCentre;
        private float m_fRadius;

        public Circle()
        {
            m_vecCentre = Vector2.Zero;
            m_fRadius = 0.0f;
        }

        public Circle(Circle p_circle)
        {
            m_vecCentre = p_circle.Centre;
            m_fRadius = p_circle.Radius;
        }

        public Circle(Vector2 p_vecCentre, float p_fRadius)
        {
            m_vecCentre = p_vecCentre;
            m_fRadius = p_fRadius;
        }

        public Vector2 Centre
        {
            get { return m_vecCentre; }
            set { m_vecCentre = value; }
        }

        public float Radius
        {
            get { return m_fRadius; }
            set { m_fRadius = value; }
        }

        public float RadiusSquared
        {
            get { return m_fRadius * m_fRadius; }
        }

        #region Geometry Interface

        public bool Intersects(Geometry p_geometry)
        {
            if (p_geometry is Circle)
            {
                Circle circle = (Circle)p_geometry;
                return (circle.Centre - m_vecCentre).Length() < m_fRadius + circle.m_fRadius;
            }
            else if (p_geometry is Edge)
            {
                Edge edge = (Edge)p_geometry;
                return edge.Intersects(this);
            }
            else if (p_geometry is Polygon)
            {
                Polygon polygon = (Polygon)p_geometry;
                return polygon.Intersects(this);
            }
            else
                return false;
        }

        public void ComputeContactPoints(Geometry p_geometry,
            List<ContactPoint> p_listContactPoints)
        {
            if (p_geometry is Circle)
            {
            }
            else if (p_geometry is Edge)
            {
                Edge edge = (Edge)p_geometry;
                int nOldSize = p_listContactPoints.Count;
                edge.ComputeContactPoints(this, p_listContactPoints);
                for (int nIndex = nOldSize; nIndex < p_listContactPoints.Count; nIndex++)
                    p_listContactPoints[nIndex].Invert();
            }
            else if (p_geometry is Polygon)
            {
                Polygon polygon = (Polygon)p_geometry;
                int nOldSize = p_listContactPoints.Count;
                polygon.ComputeContactPoints(this, p_listContactPoints);
                for (int nIndex = nOldSize; nIndex < p_listContactPoints.Count; nIndex++)
                    p_listContactPoints[nIndex].Invert();
            }
        }

        public void Transform(Transformation p_transformation)
        {
            m_vecCentre = p_transformation.Apply(m_vecCentre);
        }

        public void Transform(Transformation p_transformation, Geometry p_geometrySource)
        {
            if (!(p_geometrySource is Circle))
                throw new Exception("Source Geometry must be a Circle");
            Circle circle = (Circle) p_geometrySource;
            m_vecCentre = p_transformation.Apply(circle.m_vecCentre);
            m_fRadius = circle.m_fRadius;
        }

        public Geometry TransformCopy(Transformation p_transformation)
        {
            return new Circle(p_transformation.Apply(m_vecCentre), m_fRadius);
        }

        public void ExtendBounds(ref Bounds p_bounds)
        {
            Vector2 vecMin = p_bounds.Min;
            Vector2 vecMax = p_bounds.Max;
            vecMin.X = Math.Min(vecMin.X, m_vecCentre.X - m_fRadius);
            vecMin.Y = Math.Min(vecMin.Y, m_vecCentre.Y - m_fRadius);
            vecMax.X = Math.Max(vecMax.X, m_vecCentre.X + m_fRadius);
            vecMax.Y = Math.Max(vecMax.Y, m_vecCentre.Y + m_fRadius);
            p_bounds.Min = vecMin;
            p_bounds.Max = vecMax;
        }

        public Circle GetBoundingCircle(Vector2 p_vecOrigin)
        {
            Circle circle = new Circle(p_vecOrigin, 0.0f);
            circle.Radius = (m_vecCentre - p_vecOrigin).Length() + m_fRadius;
            return circle;
        }

        public Bounds Bounds
        {
            get
            {
                Vector2 vecOffset = new Vector2(m_fRadius, m_fRadius);
                return new Bounds(m_vecCentre - vecOffset, m_vecCentre + vecOffset);
            }
        }

        #endregion
    }
}
